package com.yexx.starter.security.basic;

import com.yexx.starter.security.basic.constant.SecurityConstants;
import com.yexx.starter.security.basic.util.AuthUtils;
import com.yexx.utils.CacheUtil;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Set;

/**
 * Description: 自定义抽象登录handler
 *
 * @author: zuomin (myleszelic@outlook.com)
 * @date: 2020/11/02 16:08
 */
public abstract class BasicLoginFilter extends AbstractAuthenticationProcessingFilter {

    private SecurityUserDetailsService userDetailsService;

    private Boolean singleSignOn = false;

    /**
     * @param defaultFilterProcessesUrl 要拦截的url
     * @param failureUrl
     */
    protected BasicLoginFilter(String defaultFilterProcessesUrl, String failureUrl, Boolean singleSignOn) {
        super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.POST.name()));
        this.singleSignOn = singleSignOn;
//        setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(failureUrl));
    }

    public abstract CacheUtil getCacheUtil();

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //设置安全上下文。在当前的线程中，任何一处都可以通过SecurityContextHolder 来获取当前用户认证成功的Authentication对象
        SecurityContextHolder.getContext().setAuthentication(authResult);
        UserContext userContext = (UserContext) authResult.getPrincipal();

        if (singleSignOn) {
            siginElsewhere(userContext.getAccountId());
        }

        Calendar c = Calendar.getInstance();
        c.add(Calendar.MINUTE, AuthUtils.expire);

        //使用JWT快速生成token
        String token = AuthUtils.getToken(userContext.getAccountId().toString());

        String tokenKey = AuthUtils.getTokenKey(userContext.getAccountId().toString(), token);
        userContext.setTokenKey(tokenKey);
        getCacheUtil().set(tokenKey, userContext, 60 * AuthUtils.expire);

        response.setHeader(SecurityConstants.TOKEN_HEADER, SecurityConstants.HEAD + token);
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        try (PrintWriter out = response.getWriter()) {
            out.write(String.format(SecurityConstants.LOGIN_SUCCESS, token, c.getTime().getTime()));
        }

        if (userDetailsService != null) {
            try {
                userDetailsService.loginSuccessful(request, response, authResult);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        out.write(String.format(SecurityConstants.LOGIN_FAIL, failed.getMessage()));
        out.flush();
    }

    // 将token设置为在其他地方登陆
    private void siginElsewhere(Long userId) {

        if (userId == null) return;

        try {
            String tokenKey = AuthUtils.getTokenKey(userId.toString(), "*");
            Set<String> keys = getCacheUtil().keys(tokenKey);
            if (keys != null && keys.size() > 0) {
                for (String key : keys) {
//                    Object o = getCacheUtil().get(key);
                    UserContext context = (UserContext) getCacheUtil().get(key);
                    if (!context.getSiginElsewhere()) {
                        long expire = getCacheUtil().getExpire(key);
                        context.setSiginElsewhere(true);
                        getCacheUtil().set(key, context, expire);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //获取手机号
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(SecurityConstants.PARAMETER.LOGIN_PHONE_NUM);
    }

    //获取登录设备信息
    protected String obtainDeviceUuid(HttpServletRequest request) {
        String deviceUuid = request.getHeader(SecurityConstants.PARAMETER.DEVICE_UUID);
        if (StringUtils.isEmpty(deviceUuid)) {
            deviceUuid = request.getParameter(SecurityConstants.PARAMETER.DEVICE_UUID);
        }
        return deviceUuid;
    }

    protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setUserDetailsService(SecurityUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

}
